package pl.koziolekweb.szkola.FDKolekcjePliki;

// FD-KolekcjePliki/Lexems.java
 
/**
 * Program can be invoked with one argument (name of a text
 * file); if not specified, the file 'alice.txt' is assumed.
 *
 * We read the input file line by line, delete non-letters,
 * and add words to a hash representing words in the text;
 * words (in lowercase) are keys, and objects of class Word
 * are values (the objects contain number of occurrences).
 * Then the hash is converted into an array, sorted (all
 * different words in order of the number of occurrences)
 * and written into file 'Words.out'. One letter words
 * are ignored.
*/

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.StringTokenizer;

public class Lexems {

    Map<String,Word> hash = new HashMap<String,Word>();

    public static void main(String[] args) {
        String filename;
        if ( args.length == 0 ) filename = "alice.txt";
        else                    filename = args[0];
        new Lexems(filename);
    }


    public Lexems(String filename) {

        int numWord        = 0;
        int numDiffWords = 0;
        int numLines       = 0;

        File filein = new File(filename);
        File filout = new File("Words.out");

        if ( !filein.exists()     ||
             !filein.canRead()    ||
              filein.isDirectory()  ) {
            System.out.println("Invalid file !!!");
            System.exit(1);
        }

        // reading the file, creating hash (method toHash)
        BufferedReader br = null;
        try {
            br = new BufferedReader(
                     new FileReader(filein));
            String line;
            while ( (line = br.readLine()) != null) {
                numWord += toHash(line);
                numLines++;
            }
        } catch (IOException e) {
            System.out.println("Problems with reading");
            e.printStackTrace();
            System.exit(1);
        } finally {
            try { if (br != null) br.close(); }
            catch(IOException ignore) { }
        }

        // converting hash to array and sorting
        Word[] arr = hash.values().toArray(new Word[]{});
        Arrays.sort(arr);

        numDiffWords = arr.length;

        // results to file 'Words.out'
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(filout));
            bw.write(
                "File " + filename + " contains " +
                numLines + " lines.\nNumber of words is " +
                numWord + ", of which " + numDiffWords +
                " are different.\n\n");
            bw.write("  Occurrences    Word\n" +
                     "---------------  ----\n");
            for ( int i =0; i < numDiffWords; i++ )
                bw.write(arr[i] + "\n");
            System.out.println(
                "\nResults in file \"" + filout + "\".");
        } catch (Exception e) {
            System.out.println("Problems with writing!!!");
            e.printStackTrace();
            System.exit(1);
        } finally {
            try { if (bw != null) bw.close(); }
            catch(IOException ignore) { }
        }
    }

    private int toHash(String line) {
          // deleting non-letters
        StringBuffer sb =
                new StringBuffer(line.toLowerCase());
        for (int i = 0; i < sb.length(); i++)
            if ( !Character.isLetter(sb.charAt(i)) )
                sb.setCharAt(i,' ');

        StringTokenizer st =
                new StringTokenizer(sb.toString());
        String s;
        int ile = 0;
        while ( st.hasMoreTokens() ) {
            s = st.nextToken();
            if ( s.length() < 2 ) continue;
            ile++;
            if (hash.containsKey(s))
                hash.get(s).addOne();
            else
                hash.put(s,new Word(s));
        }
        return ile;
    }

    class Word implements Comparable<Word> {

        final private String s;
        private int count=1;

        public Word(String s) {
            this.s = s;
        }

        public void addOne() {
            count++;
        }

        public int compareTo(Word word) {
            if ( count == word.count ) {
                return s.compareTo(word.s);
            } else {
                return word.count - count;
            }
        }

          // defining toString so Word's are automatically
          // converted into String'a in methods println(...)
          // or write(...)
        public String toString() {
            StringBuffer sb = new StringBuffer(
                "                  ");
            String n = "" + count;
            int d = n.length();
            return sb.replace(12-d-1,12,n).
                      append(s).toString();
        }
    }
}
